﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Security;
using System.Collections;
using MongoSecuretyProvider.Entity;

namespace MongoDB.Securety
{
    public class MongoRoleProvider : RoleProvider
    {
        private readonly string UserCollectionName = "system.users";
        private readonly string RolesCollectionName = "Roles";
        private readonly string UserRolesCollectionName = "UserRoles";
        private string databaseName ;
        private object syncObj = new object();
        private Hashtable userRoles = new Hashtable(50);

        public override string ApplicationName
        {
            get
            {
                return ((MongoMembershipProvider)Membership.Provider).ApplicationName;
            }
            set
            {
                ((MongoMembershipProvider)Membership.Provider).ApplicationName = value;
            }
        }

        public override string Description
        {
            get
            {
                return base.Description;
            }
        }

        public override string Name
        {
            get
            {
                return base.Name;
            }
        }

        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {
            base.Initialize(name, config);
            foreach (string key in config)
            {
                switch (key.ToLower())
                {
                    case "databasename":
                        databaseName = config[key].ToString();
                        break;
                }
            }
        }

        public override void CreateRole(string roleName)
        {
            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo .Connect ();
                    IMongoDatabase db = mongo[databaseName];                                        
                    IMongoCollection roles = db.GetCollection(RolesCollectionName);
                    
                    if (null != roles.FindOne(new Document().Add("RoleName", roleName)))
                        throw new Exception(string.Format("Роль {0} уже существует в системе", roleName));
                    else 
                        roles.Insert (new Document ().Add ("RoleName",roleName ));

                }
                finally
                {
                    mongo.Disconnect();
                }
            }
            
        }

        
        

        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {

            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection users = db.GetCollection(UserCollectionName);
                    IMongoCollection roles = db.GetCollection(RolesCollectionName);

                    // проверить существование указанных пользователей
                    foreach (string item in usernames)
                    {
                        if (null == users.FindOne(new Document().Add("user", item)))                        
                            throw new Exception(string.Format("Пользователь {0} не существует", item));
                    }
                    //проверить наличие роли
                    foreach (string item in roleNames )
                    {                    
                    if(null==roles.FindOne (new Document().Add("RoleName", item )))
                        throw new Exception(string.Format("Роль {0} не существует", item));
                    }

                    foreach (string userItem in usernames)
                    {
                        //вставка или обновление
                        IMongoCollection userRoles = db.GetCollection(UserRolesCollectionName);
                        UserRole objUserRole = null;
                        bool bExistst = false;
                        Document docUserRole = userRoles.FindOne(new Document().Add("user", userItem));
                        //UserRole не существует
                        if (null == docUserRole) 
                        {
                            objUserRole = new UserRole() { user = usernames[0] };                            
                            objUserRole.roles = new List<string>();
                            foreach (string roleItem in roleNames)
                            {
                                objUserRole.roles.Add(roleNames[0]);
                                //docUserRole = DocumentConvertor.DocumentFromObject(objUserRole);
                                userRoles.Insert(docUserRole);
                            }
                        }
                        else
                        {
                            //сформировать из документа объект
                           // objUserRole = docUserRole.ToClass<UserRole>();
                            if (null == objUserRole.roles)
                                objUserRole.roles = new List<string>();
                            
                            foreach (string roleItem in roleNames)
                            {
                                //проверить роль на существование
                                bExistst = objUserRole.roles.Contains(roleItem);
                                if (!bExistst)
                                    objUserRole.roles.Add(roleItem);
                            }

                            // обновить 
                            //Document updUserRole = MongoDB.Tools.DocumentConvertor.DocumentFromObject(objUserRole);
                            //userRoles.FindAndModify(updUserRole, new Document().Add("user", objUserRole.user));

                        }
                    }

                }
                finally
                {
                    mongo.Disconnect();
                }
            }
            
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {

            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection users = db.GetCollection(UserCollectionName);
                    IMongoCollection roles = db.GetCollection(RolesCollectionName);

                    // проверить существование указанных пользователей
                    foreach (string item in usernames)
                    {
                        if (null == users.FindOne(new Document().Add("user", item)))
                            throw new Exception(string.Format("Пользователь {0} не существует", item));
                    }
                    //проверить наличие роли
                    foreach (string item in roleNames)
                    {
                        if (null == roles.FindOne(new Document().Add("RoleName", item)))
                            throw new Exception(string.Format("Роль {0} не существует", item));
                    }

                    foreach (string userItem in usernames)
                    {
                        //вставка или обновление
                        IMongoCollection userRoles = db.GetCollection(UserRolesCollectionName);
                        UserRole objUserRole = null;                        
                        Document docUserRole = userRoles.FindOne(new Document().Add("user", userItem));
                        //UserRole не существует
                        if (null == docUserRole)
                        {
                            continue;                        
                        }
                        else
                        {
                            //сформировать из документа объект
                          //  objUserRole = docUserRole.ToClass<UserRole>();
                            if (null == objUserRole.roles)
                                objUserRole.roles = new List<string>();

                            foreach (string roleItem in roleNames)
                            {                                
                                objUserRole.roles.Remove (roleItem);                                
                                    
                            }

                            // обновить 
                           // Document updUserRole = MongoDB.Tools.DocumentConvertor.DocumentFromObject(objUserRole);
                            //userRoles.FindAndModify(updUserRole, new Document().Add("user", objUserRole.user));

                        }
                    }

                }
                finally
                {
                    mongo.Disconnect();
                }
            }
            
            
        }
        

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {   
            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection roles = db.GetCollection(RolesCollectionName);
                    roles.Delete(new Document().Add("RoleName", roleName));
                    return true;
                }
                catch
                {
                    return false;
                }
                finally
                {
                    mongo.Disconnect();
                    
                }
            }
            
        }

        public override bool IsUserInRole(string username, string roleName)
        {

            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection roles = db.GetCollection(RolesCollectionName);
                    string[] rolesQ= {roleName};
                    Document inQ = new Document().Add("$in", rolesQ);
                    return (null != roles.FindOne((new Document().Add("roles", inQ))));
                }
                finally
                {
                    mongo.Disconnect();

                }
            }
                 
        }

        public override bool RoleExists(string roleName)
        {
            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection roles = db.GetCollection(RolesCollectionName);
                    return (null!=roles.FindOne ((new Document().Add("RoleName", roleName))));                    
                }                
                finally
                {
                    mongo.Disconnect();

                }
            }
        }
                
        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            return null;
        }

        public override string[] GetAllRoles()
        {
            return null;          
        }

        public void ResetRolesCache(string username)
        {
            lock (syncObj)
            {
                if (userRoles.Contains(username))
                {
                    userRoles.Remove(username);
                }
            }
        }

        public override string[] GetRolesForUser(string username)
        {   
            Document doc = getRolesOfUser(username );
            if(doc ==null)
                return new string[0];

            List<string> lst = (List<string>)doc["roles"];
            if (null == lst)
                return new string[0];
            else
                return lst.ToArray <string >();          
            
        }

        public override string[] GetUsersInRole(string roleName)
        {
            string [] r={roleName};
            List<string> result = new List<string>();
            List<Document > cur= getUsersOfRoles(r);
            foreach (Document item in cur)
            {
                object obj= item["user"];
                result.Add(obj.ToString());
            }
            return result.ToArray<string>();
        }


        /// <summary>
        /// вернуть userRoles - 
        /// </summary>
        /// <param name="roleNames"></param>
        /// <param name="userNames"></param>
        /// <returns></returns>
        private List <Document> getUsersOfRoles(string[] roleNames)
        {
            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;
            List<Document> result = new List<Document>();

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection userRoles = db.GetCollection(UserRolesCollectionName);
                    Document inQ = new Document().Add("$in", roleNames);
                    ICursor resultQ = userRoles.Find((new Document().Add("roles", inQ)));
                    foreach (Document item in resultQ.Documents) {
                        result.Add(item);
                    }
                    return result;
                }
                finally
                {
                    mongo.Disconnect();

                }
            }
        }

        private Document getRolesOfUser(string userName )
        {
            MongoConnectionStringBuilder builder = MongoMembershipProvider.getCurrentConnectionString(databaseName); ;
            Document result = null;

            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection userRoles = db.GetCollection(UserRolesCollectionName);                    
                    result = userRoles.FindOne((new Document().Add("user", userName )));                    
                    return result;
                }
                finally
                {
                    mongo.Disconnect();

                }
            }
        }

        public List<string> GetRolesOfUser(string userName,MongoConnectionStringBuilder builder)
        {
            List<string> result = new List<string>();
            Document doc = null;
            using (Mongo mongo = new Mongo(builder.ToString()))
            {
                try
                {
                    mongo.Connect();
                    IMongoDatabase db = mongo[databaseName];
                    IMongoCollection userRoles = db.GetCollection(UserRolesCollectionName);
                    doc  = userRoles.FindOne((new Document().Add("user", userName)));
                    if (null == doc)
                        return result;
                    result  = (List<string>)doc["roles"];
                    return result;
                }
                finally
                {
                    mongo.Disconnect();

                }
            }
        }
        
    }

    

}
